/**
 * Licensed under the Apache License Version 2.0.
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package net.interknit.knitter.client.java;

import java.io.File;

import net.interknit.knitter.client.CodeGenTask;
import net.interknit.knitter.client.layout.EndpointEl;
import net.interknit.knitter.client.layout.MethodEl;
import net.interknit.knitter.client.layout.TypeEl;


/**
 *
 */
public class JavaEndpointSourceWriter extends JavaTypeSourceWriter
{
	@Override
	public void write(File srcDir, String packageName)
	{
		try
		{
			super.write(srcDir, packageName);

			for(TypeEl type : ((EndpointEl) getTypeEl()).getTypes())
			{
				CodeGenTask.getSourceWriterFactory().create(type).write(srcDir, packageName);
			}

			// write the WireAdapter.java file
			new WireAdapterWriter(getTypeEl().getName()).write(srcDir, packageName);

			// write the ClientMain.java file
			new ClientMainWriter(
					getTypeEl().getName(),
					((EndpointEl) getTypeEl()).getUrl())
			.write(srcDir, packageName);
		}
		catch(Exception e)
		{
			throw new RuntimeException(e);
		}
	}

	@Override
	protected void writeCode(String packageName)
	{
		String unqualifiedClassName = unqualifiedClassName();

		if(! "".equals(packageName.trim()))
			println("package " + packageName + ";");

		println("import javax.ws.rs.core.Response;");
		println("import org.apache.cxf.jaxrs.client.WebClient;");

		print("public class " + unqualifiedClassName);
		print("{");
		print("\t" + "private static final java.lang.String propertiesResource =");
		print("\t" + "\t" + "\"" + unqualifiedClassName + ".properties\";");
		print("");

		print("\t" + "/**");
		print("\t" + " * Create a new service stub object");
		print("\t" + " * @param baseAddress Base address (URL) where the service is running, ");
		print("\t" + " * for example, http://some.host:12345/orderService");
		print("\t" + " */");
		print("\t" + "public " + unqualifiedClassName + "(String baseAddress)");
		print("\t" + "{");
		print("\t" + "\t" + "loadProperties();");
		print("");
		print("\t" + "\t" + "if(baseAddress == null || \"\".equals(baseAddress))");
		print("\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "baseAddress = \"http://localhost\";");
		print("\t" + "\t" + "\t" + "System.out.println(\"Base address (URL) for the service is null, will try to set it to \" + baseAddress);");
		print("\t" + "\t" + "}");
		print("");
		print("\t" + "\t" + "baseAddress = baseAddress.endsWith(\"/\") ? baseAddress : baseAddress + \"/\";");
		print("");

		for(MethodEl method : ((EndpointEl) getTypeEl()).getMethods())
		{
			JavaMethodSourceWriter methodSourceWriter = new JavaMethodSourceWriter();
			methodSourceWriter.setTypeEl(method);

			print("\t" + "\t" + methodSourceWriter.invokerName() + " = setAuth(WebClient.create(baseAddress + \"" + method.getPath() + "\"));");
		}

		println("\t" + "}");

		for(MethodEl method : ((EndpointEl) getTypeEl()).getMethods())
		{
			JavaMethodSourceWriter methodSourceWriter = new JavaMethodSourceWriter();
			methodSourceWriter.setTypeEl(method);

			print(methodSourceWriter.source());
			print("");
		}

		print("\t" + "/**");
		print("\t" + " * Sets authentication attributes for the given WebClient object");
		print("\t" + " */");
		print("\t" + "protected WebClient setAuth(WebClient client)");
		print("\t" + "{");
		print("\t" + "\t" + "return client;" + "// no implementation here. hook for the sub-class");
		print("\t" + "}");
		print("");

		print("\t" + "/**");
		print("\t" + " * Hook method for the sub-class to set additional parameters, headers,");
		print("\t" + " * or cookies on the live WebClient object");
		print("\t" + " */");
		print("\t" + "protected void setAdditionalInfo(WebClient client)");
		print("\t" + "{");
		print("\t" + "\t" + ";" + "// no implementation here. hook for the sub-class");
		print("\t" + "}");
		print("");

		print("\t" + "/**");
		print("\t" + " * Loads custom property values from " + unqualifiedClassName + ".properties on the classpath");
		print("\t" + " */");
		print("\t" + "protected void loadProperties()");
		print("\t" + "{");
		print("\t" + "\t" + "try");
		print("\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "java.io.InputStream is = getClass().getClassLoader().getResourceAsStream(propertiesResource);");
		print("");
		print("\t" + "\t" + "\t" + "if(is != null)");
		print("\t" + "\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "\t" + "java.util.Properties p = new java.util.Properties();");
		print("\t" + "\t" + "\t" + "\t" + "p.load(is);");
		print("");
		print("\t" + "\t" + "\t" + "\t" + "for(java.util.Iterator<java.lang.Object> it = p.keySet().iterator(); it.hasNext(); )");
		print("\t" + "\t" + "\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "\t" + "\t" + "java.lang.String k = (String) it.next();");
		print("\t" + "\t" + "\t" + "\t" + "\t" + "java.lang.String v = p.getProperty(k);");
		print("");
		print("\t" + "\t" + "\t" + "\t" + "\t" + "if(v != null && ! \"\".equals(v.trim()))");
		print("\t" + "\t" + "\t" + "\t" + "\t" + "\t" + "java.lang.System.setProperty(k, v);");
		print("\t" + "\t" + "\t" + "\t" + "}");
		print("\t" + "\t" + "\t" + "}");
		print("\t" + "\t" + "}");
		print("\t" + "\t" + "catch(java.lang.Exception e)");
		print("\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "java.lang.System.out.println(propertiesResource + \" not found on the classpath, \" +");
		print("\t" + "\t" + "\t" + "\t" + "\t" + "\"custom properties will be not loaded.\");");
		print("\t" + "\t" + "}");
		print("\t" + "}");
		print("");

		print("\t" + "/**");
		print("\t" + " * Checks the returned Response object to see if it indicates a server- or client- error.");
		print("\t" + " * If so, throws RuntimeException to indicate the error with the HTTP status code.");
		print("\t" + " */");
		print("\t" + "protected void checkFault(Response response)");
		print("\t" + "{");
		print("\t" + "\t" + "if(response != null)");
		print("\t" + "\t" + "{");
		print("\t" + "\t" + "\t" + "int code = response.getStatus();");
		print("");
		print("\t" + "\t" + "\t" + "String msg = null;");
		print("\t" + "\t" + "\t" + "if(code > 399 && code < 500) msg = \"Client\";");
		print("\t" + "\t" + "\t" + "else if(code > 499 && code < 600) msg = \"Server\";");
		print("");
		print("\t" + "\t" + "\t" + "if(msg != null)");
		print("\t" + "\t" + "\t" + "\t" + "throw new RuntimeException(msg + \" Error. HTTP response code: \" + code);");
		print("\t" + "\t" + "\t" + "else");
		print("\t" + "\t" + "\t" + "\t" + "System.out.println(\"Invocation successful.  HTTP response code: \" + code);");
		print("\t" + "\t" + "}");
		print("\t" + "}");
		print("");

		print("\t" + "/**");
		print("\t" + " * Shorthand for String.valueOf(object)");
		print("\t" + " */");
		print("\t" + "private String str(java.lang.Object obj)");
		print("\t" + "{");
		print("\t" + "\t" + "return String.valueOf(obj);");
		print("\t" + "}");
		print("");

		print("\t" + "/**");
		print("\t" + " * Log the invocation URI for debugging.  This log will be disabled if logging level is above INFO.");
		print("\t" + " */");
		print("\t" + "protected void logInvocation(WebClient wc)");
		print("\t" + "{");
		print("\t" + "\t" + "System.out.println(\"Invoking: \" + wc.getCurrentURI().toString());");
		print("\t" + "}");
		print("}");
	}
}
